#include <vector>
#include <set>
#include "MapRenderer.h"

#include <QtCore>

using namespace std;

namespace opennav {

MapRenderer::MapRenderer() {

}

MapRenderer::~MapRenderer() {

}

int MapRenderer::render(const MapSelection& selection,
		const Transformation& transformation, CanvasInterface& image) {
	return render(selection, transformation, image, NULL);
}

int MapRenderer::render(const MapSelection& selection,
		const Transformation& transformation, CanvasInterface& image,
		const Route* route) {
	int pts = 0;
	vector<MapPoint*> layer_cities_big, layer_cities_small;
	vector<MapShape*> layer_shapes, layer_buildings;
	vector<MapLine*> layer_highway0, layer_highway1, layer_highway2,
			layer_highway3, layer_highway4, layer_highway5, layer_highway6,
			layer_lines;

	vector<vector<Point>*> layer_hw0, layer_hw1, layer_hw2, layer_hw3,
			layer_hw4, layer_hw5, layer_hw6;

	int level = transformation.scale();
	street_label_map street_labels;

	for (vector<MapObject*>::const_iterator item = selection.items.begin(); item
			!= selection.items.end(); item++) {
		switch ((**item).type()) {
		case LINE: {
			MapLine* line = (MapLine*) *item;
			switch (line->line_type) {
			case Line_Motorway: {
				std::vector<Point>* points = new std::vector<Point>();
				transformation.transform(line->coordinates, *points);
				layer_hw0.push_back(points);
				if (level < 96) {
					place_street_label(line, image, *points, street_labels);
				}
			}
				break;
				//layer_highway0.push_back(line); break;
			case Line_Highway1: {
				std::vector<Point>* points = new std::vector<Point>();
				transformation.transform(line->coordinates, *points);
				layer_hw1.push_back(points);
				if (level < 96) {
					place_street_label(line, image, *points, street_labels);
				}
			}
				//layer_highway1.push_back(line); break;
			case Line_Highway2: {
				std::vector<Point>* points = new std::vector<Point>();
				transformation.transform(line->coordinates, *points);
				layer_hw2.push_back(points);
				if (level < 96) {
					place_street_label(line, image, *points, street_labels);
				}
			}
				//layer_highway2.push_back(line); break;
			case Line_Highway3: {
				std::vector<Point>* points = new std::vector<Point>();
				transformation.transform(line->coordinates, *points);
				layer_hw3.push_back(points);
				if (level < 96) {
					place_street_label(line, image, *points, street_labels);
				}
			}
				//layer_highway3.push_back(line); break;
			case Line_Highway4: {
				std::vector<Point>* points = new std::vector<Point>();
				transformation.transform(line->coordinates, *points);
				layer_hw4.push_back(points);
				if (level < 96) {
					place_street_label(line, image, *points, street_labels);
				}
			}
				//layer_highway4.push_back(line); break;
			case Line_Highway5: {
				std::vector<Point>* points = new std::vector<Point>();
				transformation.transform(line->coordinates, *points);
				layer_hw5.push_back(points);
				if (level < 96) {
					place_street_label(line, image, *points, street_labels);
				}
			}
				//layer_highway5.push_back(line); break;
			case Line_Track: {
				std::vector<Point>* points = new std::vector<Point>();
				transformation.transform(line->coordinates, *points);
				layer_hw6.push_back(points);
				if (level < 96) {
					place_street_label(line, image, *points, street_labels);
				}
			}
				//layer_highway6.push_back(line); break;
			default:
				layer_lines.push_back(line);
				break;
			}
		}
			break;
		case SHAPE: {
			MapShape* shape = (MapShape*) *item;
			if (shape->shape_type == Shape_Building)
				layer_buildings.push_back(shape);
			else
				layer_shapes.push_back(shape);
		}
			break;
		case POINT: {
			MapPoint* point = (MapPoint*) *item;
			switch (point->point_type) {
			case Point_City1:
				layer_cities_big.push_back(point);
				break;
			case Point_City2:
			case Point_City3:
			case Point_City4:
			case Point_Suburb:
				layer_cities_small.push_back(point);
				break;
			default:
				break;
			}
			//layerPoints.push_back(point);
		}
			break;
		case UNKNOWN:
			// should never be reached
			break;
		}
	}
	{
		Color backgroundcolor(COLOR_BACKGROUND);
		image.set_foreground_color(backgroundcolor);
		image.set_background_color(backgroundcolor);
		image.draw_rectangle(Point(0, 0), image.width(), image.height());
	}

	int motorwayWidth = 12;
	if (motorwayWidth)

	{
		/*
		 * Shapes
		 */
		for (std::vector<MapShape*>::iterator shape = layer_shapes.begin(); shape
				!= layer_shapes.end(); shape++) {
			int color = 0x800000;
			switch ((*shape)->shape_type) {
			case Shape_Beach:
				color = 0xf2f1f7;
				break;
			case Shape_Cemetary:
				color = 0x32cd32;
				break;
			case Shape_Cultivation:
				color = 0xf0fff0;
				break;
			case Shape_Dam:
				color = 0xafafaf;
				break;
			case Shape_Farming:
				color = 0xdeb887;
				break;
			case Shape_Forest:
				color = 0x8dc56c;
				break;
			case Shape_Glacier:
				color = 0xf0f8ff;
				break;
			case Shape_Grass:
				color = 0xCFECA8;
				break;
			case Shape_Industrial:
				color = 0xd3d3d3;
				break;
			case Shape_Lake:
				color = 0x6495ed;
				break;
			case Shape_Military:
				color = 0x808000;
				break;
			case Shape_Recreation:
				color = 0x90ee90;
				break;
			case Shape_Residential:
				color = 0xdcdcdc;
				break;
			case Shape_River:
				color = 0x6495ed;
				break;
			case Shape_Shopping:
				color = 0xfff0f5;
				break;
			case Shape_Wetland:
				color = 0xcfeca8;
				break;
			}
			Color background(color);
			image.set_background_color(background);
			image.set_foreground_color(background);
			std::vector<Point> points;
			transformation.transform((*shape)->coordinates, points);
			image.draw_polygon(points);
			//ImageUtils::draw_polygon_clipped(image, points);
			pts++;
		}
	}
	{
		/*
		 * Buildings
		 */
		Color foreground(0xbba0a0);
		Color background(0xccbbbb);
		image.set_foreground_color(foreground);
		image.set_background_color(background);
		for (std::vector<MapShape*>::iterator shape = layer_buildings.begin(); shape
				!= layer_buildings.end(); shape++) {
			std::vector<Point> points;
			transformation.transform((*shape)->coordinates, points);
			image.draw_polygon(points);
			pts++;
		}
	}

	float scale_roads = max(min(256.0 / transformation.scale(), 6.0), 1.0);
	{
		image.set_line_width(4);
		for (std::vector<MapLine*>::iterator line = layer_lines.begin(); line
				!= layer_lines.end(); line++) {
			std::vector<Point> points;
			transformation.transform((*line)->coordinates, points);
			switch ((*line)->line_type) {
			case Line_Weir:
				line_weir(image, points, level);
				break;
			case Line_Stream:
				line_stream(image, points, level);
				break;
			case Line_River:
				line_river(image, points, level);
				break;
			case Line_Monorail:
				line_monorail(image, points, level);
				break;
			case Line_Rail:
				line_rail(image, points, level);
				break;
			case Line_Funicular:
				line_funicular(image, points, level);
				break;
			case Line_Tram:
				line_tram(image, points, level);
				break;
			case Line_Subway:
				line_subway(image, points, level);
				break;
			case Line_Raceway:
				line_raceway(image, points, level);
				break;
			case Line_BorderCountry:
				line_border_country(image, points, level);
				break;
			}
		}
	}

	//	{
	//		Color color(LINE_HWTYPE6_COLOR);
	//		image.set_foreground_color(color);
	//		image.set_line_width(3 * scale_roads);
	//		for (std::vector<MapLine*>::iterator line = layer_highway6.begin(); line != layer_highway6.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//		}
	//	}
	//	{
	//		Color color(LINE_HWTYPE5_COLOR);
	//		image.set_foreground_color(color);
	//		image.set_line_width(6 * scale_roads);
	//		for (std::vector<MapLine*>::iterator line = layer_highway5.begin(); line != layer_highway5.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//			if (level < 96) {
	//				place_street_label(*line, image, points, street_labels);
	//			}
	//		}
	//	}
	//	{
	//		Color color(LINE_HWTYPE4_COLOR);
	//		image.set_foreground_color(color);
	//		image.set_line_width(8 * scale_roads);
	//		for (std::vector<MapLine*>::iterator line = layer_highway4.begin(); line != layer_highway4.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//			if (level < 96) {
	//				place_street_label(*line, image, points, street_labels);
	//			}
	//		}
	//	}
	{
		draw_hw_layer(Color(0x000000), 3 * scale_roads + 4, layer_hw6, image,
				false);
		draw_hw_layer(Color(0x000000), 6 * scale_roads + 4, layer_hw5, image,
				false);
		draw_hw_layer(Color(0x000000), 8 * scale_roads + 4, layer_hw4, image,
				false);
		draw_hw_layer(Color(0x000000), 8 * scale_roads + 4, layer_hw3, image,
				false);
		draw_hw_layer(Color(0x000000), 8 * scale_roads + 4, layer_hw2, image,
				false);
		draw_hw_layer(Color(0x000000), 10 * scale_roads + 4, layer_hw1, image,
				false);
		draw_hw_layer(Color(0x000000), 12 * scale_roads + 4, layer_hw0, image,
				false);

		draw_hw_layer(Color(LINE_HWTYPE6_COLOR), 3 * scale_roads, layer_hw6,
				image, true);
		draw_hw_layer(Color(LINE_HWTYPE5_COLOR), 6 * scale_roads, layer_hw5,
				image, true);
		draw_hw_layer(Color(LINE_HWTYPE4_COLOR), 8 * scale_roads, layer_hw4,
				image, true);
		draw_hw_layer(Color(LINE_HWTYPE3_COLOR), 8 * scale_roads, layer_hw3,
				image, true);
		draw_hw_layer(Color(LINE_HWTYPE2_COLOR), 8 * scale_roads, layer_hw2,
				image, true);
		draw_hw_layer(Color(LINE_HWTYPE1_COLOR), 10 * scale_roads, layer_hw1,
				image, true);
		draw_hw_layer(Color(LINE_HWTYPE0_COLOR), 12 * scale_roads, layer_hw0,
				image, true);

		draw_route(selection, transformation, image, route);
	}
	//	{
	//		Color color(LINE_HWTYPE3_COLOR);
	//		Color color_b(0x000000);
	//		image.set_foreground_color(color_b);
	//		int width = 8 * scale_roads;
	//		image.set_line_width(width + 6);
	//		for (std::vector<MapLine*>::iterator line = layer_highway3.begin(); line != layer_highway3.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//			if (level < 96) {
	//				place_street_label(*line, image, points, street_labels);
	//			}
	//		}
	//		image.set_foreground_color(color);
	//		image.set_line_width(width);
	//		for (std::vector<MapLine*>::iterator line = layer_highway3.begin(); line != layer_highway3.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//		}
	//	}
	//	{
	//		Color color(LINE_HWTYPE2_COLOR);
	//		image.set_foreground_color(color);
	//		image.set_line_width(8 * scale_roads);
	//		for (std::vector<MapLine*>::iterator line = layer_highway2.begin(); line != layer_highway2.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//			if (level < 96) {
	//				place_street_label(*line, image, points, street_labels);
	//			}
	//		}
	//	}
	//	{
	//		Color color(LINE_HWTYPE1_COLOR);
	//		image.set_foreground_color(color);
	//		image.set_line_width(10 * scale_roads);
	//		for (std::vector<MapLine*>::iterator line = layer_highway1.begin(); line != layer_highway1.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//			if (level < 96) {
	//				place_street_label(*line, image, points, street_labels);
	//			}
	//		}
	//	}
	//	{
	//		Color color(LINE_HWTYPE0_COLOR);
	//		image.set_foreground_color(color);
	//		image.set_line_width(12 * scale_roads);
	//		for (std::vector<MapLine*>::iterator line = layer_highway0.begin(); line != layer_highway0.end(); line++) {
	//			std::vector<Point> points;
	//			transformation.transform((*line)->coordinates(), points);
	//			image.draw_lines(points);
	//		}
	//	}
	{
		Color color(LABEL_CITY_LARGE_COLOR);
		Font font = Font(30, FONT_PLAIN);
		image.set_foreground_color(color);
		image.set_font(font);
		image.set_line_width(1);
		for (std::vector<MapPoint*>::iterator point = layer_cities_big.begin(); point
				!= layer_cities_big.end(); point++) {
			Point p = transformation.transform((*point)->coordinate);
			image.draw_circle(p, 1);
			Point p1(p.x - 3, p.y - 3);
			Point p2(p.x + 2, p.y - 3);
			Point p3(p.x + 2, p.y + 2);
			Point p4(p.x - 3, p.y + 2);
			image.draw_line(p1, p2);
			image.draw_line(p2, p3);
			image.draw_line(p3, p4);
			image.draw_line(p4, p1);
			p1.x--;
			p1.y--;
			p2.x++;
			p2.y--;
			p3.x++;
			p3.y++;
			p4.x--;
			p4.y++;
			image.draw_line(p1, p2);
			image.draw_line(p2, p3);
			image.draw_line(p3, p4);
			image.draw_line(p4, p1);
			p.x += 10;
			p.y -= 15;
			const std::string label = (*point)->map->get_object_label((*point));
			image.draw_text(p, label);
		}
	}
	{
		Color color(LABEL_CITY_SMALL_COLOR);
		Font font = Font(20, FONT_PLAIN);
		image.set_foreground_color(color);
		image.set_line_width(1);
		image.set_font(font);
		for (std::vector<MapPoint*>::iterator point =
				layer_cities_small.begin(); point != layer_cities_small.end(); point++) {
			Point p = transformation.transform((*point)->coordinate);
			Point p1(p.x - 3, p.y - 3);
			Point p2(p.x + 2, p.y - 3);
			Point p3(p.x + 2, p.y + 2);
			Point p4(p.x - 3, p.y + 2);
			image.draw_line(p1, p2);
			image.draw_line(p2, p3);
			image.draw_line(p3, p4);
			image.draw_line(p4, p1);
			p1.x--;
			p1.y--;
			p2.x++;
			p2.y--;
			p3.x++;
			p3.y++;
			p4.x--;
			p4.y++;
			image.draw_line(p1, p2);
			image.draw_line(p2, p3);
			image.draw_line(p3, p4);
			image.draw_line(p4, p1);
			p.x += 10;
			p.y -= 10;
			const std::string label = (*point)->map->get_object_label((*point));
			image.draw_text(p, label);
		}
	}

	draw_street_labels(image, street_labels);

	return pts;
}

void inline MapRenderer::draw_hw_layer(Color color, const int width, vector<
		vector<Point>*>& layer, CanvasInterface &image, bool del) {
	image.set_foreground_color(color);
	image.set_line_width(width);
	for (vector<vector<Point>*>::iterator hw_it = layer.begin(); hw_it
			!= layer.end(); hw_it++) {
		image.draw_lines((**hw_it));
		if (del)
			delete (*hw_it);
	}
}

void MapRenderer::draw_route(const MapSelection& selection,
		const Transformation& transformation, CanvasInterface& image,
		const Route* route) {
	if (route == NULL)
		return;

	Map* const map = selection.map;
	const Area& bounds = selection.bounds;

	// TODO: mit pointern arbeiten anstatt umzukopieren
	std::vector<RouteSegment> segments = route->segments();
	std::vector<RouteSegment>::iterator segment_it;

	Color color(0x00FF00);
	image.set_foreground_color(color);
	image.set_line_width(10);

	for (segment_it = segments.begin(); segment_it != segments.end(); segment_it++) {
		const RouteSegment& segment = *segment_it;
		const RoadSegment& road_segment = segment.road_segment();

		std::vector<MapObjectReference> object_refs =
				road_segment.map_object_refs;
		std::vector<MapObjectReference>::iterator object_it;

		for (object_it = object_refs.begin(); object_it != object_refs.end(); object_it++) {
			const MapObjectReference& reference = *object_it;

			MapLine line;
			if (!map->get_map_object(reference, line)) {
				// couldn't find MapLine object in reference
				continue;
			}

			std::vector<Coordinate> coords = line.coordinates;
			std::vector<Coordinate>::iterator coord_it;

			Coordinate *start = NULL, *end = NULL;
			for (coord_it = coords.begin(); coord_it != coords.end(); coord_it++) {
				Coordinate* current = &(*coord_it);

				end = current;

				if (start != NULL) {
//					qDebug() << "check line (" << start->latitude << ", " << start->longitude << ") to (" << end->latitude << ", " << end->longitude << ")";
//					qDebug() << "in min-lat: " << bounds.min_latitude << " max-lat: " << bounds.max_latitude << " min-long: " << bounds.min_longitude << " max-long: " << bounds.max_longitude;
					if (bounds.containsLine(*start, *end)) {

						Point p1 = transformation.transform(*start);
						Point p2 = transformation.transform(*end);

						image.draw_line(p1, p2);
					}
				}
				start = end;
			}
		}
	}
}

void MapRenderer::place_street_label(MapLine* line, CanvasInterface& canvas,
		std::vector<Point>& points, street_label_map& labels) {
	street_label_map::iterator label_it = labels.find(line->reference);
	if (label_it == labels.end()) {
		street_label new_label;
		const std::string label = line->map->get_object_label(line);
		//		int8_t* label = new int8_t[24];
		//		label[0] = 'K';
		//		label[1] = 'a';
		//		label[2] = 'r';
		//		label[3] = 'l';
		//		label[4] = '-';
		//		label[5] = 'L';
		//		label[6] = 'i';
		//		label[7] = 'e';
		//		label[8] = 'b';
		//		label[9] = 'k';
		//		label[10] = 'n';
		//		label[11] = 'e';
		//		label[12] = 'c';
		//		label[13] = 'h';
		//		label[14] = 't';
		//		label[15] = '-';
		//		label[16] = 'S';
		//		label[17] = 't';
		//		label[18] = 'r';
		//		label[19] = 'a';
		//		label[20] = 's';
		//		label[21] = 's';
		//		label[22] = 'e';
		//		label[23] = '\0';
		Dimension dimension;
		canvas.get_text_dimension(label, dimension);
		new_label.placed = false;
		new_label.street_name = label;
		new_label.len_sq = (dimension.width + 10) * (dimension.width + 10);
		label_it = labels.insert(street_label_map::value_type(line->reference,
				new_label)).first;
	}
	if (label_it->second.placed == false) {
		for (uint32_t k = 1; k < points.size(); k++) {
			int32_t dx = points.at(k).x - points.at(k - 1).x;
			int32_t dy = points.at(k).y - points.at(k - 1).y;
			int32_t seg_len_sq = dx * dx + dy * dy;
			int32_t lbl_len_sq = label_it->second.len_sq;
			if (seg_len_sq >= lbl_len_sq) {
				if (dx < 0) {
					label_it->second.dx = -dx;
					label_it->second.dy = -dy;
					label_it->second.place.x = points.at(k).x - dx / 2;
					label_it->second.place.y = points.at(k).y - dy / 2;
					label_it->second.placed = true;
				} else {
					label_it->second.dx = dx;
					label_it->second.dy = dy;
					label_it->second.place.x = points.at(k - 1).x + dx / 2;
					label_it->second.place.y = points.at(k - 1).y + dy / 2;
					label_it->second.placed = true;
				}
				break;
			}
		}
	}
}

void MapRenderer::draw_street_labels(CanvasInterface& canvas,
		street_label_map& labels) {
	Font font = Font(20, FONT_PLAIN);
	Color color = Color(0x000000);
	canvas.set_foreground_color(color);
	canvas.set_font(font);
	for (street_label_map::iterator label_it = labels.begin(); label_it
			!= labels.end(); label_it++) {
		if (label_it->second.placed) {
			canvas.draw_text_tilted(label_it->second.place,
					label_it->second.street_name, label_it->second.dx,
					label_it->second.dy);
		}
	}
}

}
